package rollercoaster.train.tests;

import javax.media.opengl.GL;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFrame;

import rollercoaster.commons.FixedStepTessellation;
import rollercoaster.commons.Node;
import rollercoaster.commons.interfaces.NodoProducer;
import rollercoaster.environment.utils.Shader;
import rollercoaster.train.BorderTextureCoordinateEvaluator;
import rollercoaster.train.ClassicCoachProducer;
import rollercoaster.train.interfaces.IMultipleSurfaceControlPointsReader;
import rollercoaster.train.interfaces.VagoneDataReader;
import rollercoaster.train.utils.OBJControlPointsMultipleSurfacesReader;
import rollercoaster.train.utils.XMLVagoneDataReader;

import com.jogamp.opengl.util.Animator;

public class DrawCoachCookTorrance {

	public static void main(String[] args) {
		JFrame frame=new JFrame();
		
		frame.setSize(1200,1200);
		frame.setTitle("Hello Universe");

		//Costruisco un GLEventListener
		GraphicsListener listener=new GraphicsListener();
		//Costruisco un GLCanvas
		GLProfile profile=GLProfile.getGL2ES2();
		GLCanvas canvas = new GLCanvas(new GLCapabilities(profile));
		//Associo il listener al canvas
		canvas.addGLEventListener(listener);
		//Il canvas  un componente grafico!! Posso aggiungerlo al Frame
		frame.getContentPane().add(canvas);
		Animator an = new Animator(canvas);
		an.start();
		
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
	
public static class GraphicsListener implements GLEventListener{
	
	private static String vertexShader="uniform mat4 transform;"+
			"uniform mat2 textureTransform;"+
			"attribute vec3 position;"+ 
			"attribute vec2 texcoord;"+
			"varying vec2 f_texcoord;"+
			"attribute vec3 normal;"+
			"varying vec3 normale;"+
			"varying vec4 posizione;"+
			"void main(){"+
				"vec4 pos=transform*vec4(position,1);"+
				"gl_Position=pos;"+
				"normale=normal;"+ 
				"posizione=pos;"+
				"f_texcoord=textureTransform*texcoord;"+
			"}";
	
	private static String fragmentShader=""+
			"uniform sampler2D tex_color;\n"+
	"uniform mat4 transform;\n"+
	"varying vec2 f_texcoord;\n"+
	"vec4 specularContribute;\n"+
	"varying vec3 normale;\n"+
	"float NdotHV;\n"+
	"float NdotL;\n"+
	"vec4 ambient,globalAmbientMaterial,lightAmbientTotal,diffuseContribute;\n"+
	"varying vec4 posizione;\n"+
	
	"uniform vec3 lightDir;\n"+
	"uniform vec4 ambientLightModel;\n"+
	"uniform vec4 frontMaterialAmbient;\n\n"+
	"uniform vec4 diffuseLightSource;\n"+
	"uniform vec4 ambientLightSource;\n"+
	//
	"void main()\n"+
	"{\n"+
		//Imposto alcuni parametri
		"float roughnessValue = 0.2; // 0 : smooth, 1: rough\n"+
	    "float F0 = 0.5; // fresnel reflectance at normal incidence\n"+
	    "float k = 0.5;\n"+ // fraction of diffuse reflection (specular reflection = 1 - k)
	    "vec3 lightColor = vec3(0.9, 0.5, 0.1);\n"+

	    //
	    //Normalizzo di nuovo la norma
		"vec3 norm=(transform*vec4(normale,0)).xyz; \n"+
		"norm=normalize(norm);\n"+
		//
		//vec3 lightDir =gl_LightSource[0].position.xyz-posizione.xyz;
		"vec3 dirLuce=lightDir - posizione.xyz;\n"+
		//Compute dot product with light direction and the normal to the surface
		"NdotL=max(dot(dirLuce,norm),0.0);\n"+
		//Compute dot product with eye direction and half-vector
	    
		"float NdotH = max(dot(norm, normalize(dirLuce+vec3(0,0,-1))), 0.0);\n"+
		"float NdotV = max(dot(norm,vec3(0,0,-1)),0.0);\n"+
		"float VdotH = max(dot(vec3(0,0,-1),normalize(dirLuce+vec3(0,0,-1))), 0.0); \n"+
		"float mSquared = roughnessValue * roughnessValue;\n"+
		
		//Compute ambient contribute
		"ambient = frontMaterialAmbient * ambientLightSource;\n"+
		"globalAmbientMaterial = ambientLightModel * frontMaterialAmbient;\n"+
		"lightAmbientTotal= globalAmbientMaterial + ambient;\n"+

		//Compute diffuse reflection contribute (Modello di Lambert)
		"diffuseContribute=frontMaterialAmbient*NdotL*diffuseLightSource;\n"+
		
		// geometric attenuation
	        "float NH2 = 2.0 * NdotH;\n"+
	        "float g1 = (NH2 * NdotV) / VdotH;\n"+ //(2*NdotH*NdotV)/VdotH
	        "float g2 = (NH2 * NdotL) / VdotH;\n"+ //(2*NdotH*NdotL)/VdotH
	        "float geoAtt = min(1.0, min(g1, g2));\n"+
	        
		  // roughness (or: microfacet distribution function)
	        // beckmann distribution function
	        "float r1 = 1.0 / ( 3.14 * mSquared * pow(NdotH, 4.0));\n"+
	        "float r2 = (NdotH * NdotH - 1.0) / (mSquared * NdotH * NdotH);\n"+
	        "float roughness = r1 * exp(r2);\n"+
	        
	        // fresnel
	        // Schlick approximation
	        "float fresnel = pow(1.0 - VdotH, 5.0);\n"+
	        "fresnel *= (1.0 - F0);\n"+
	        "fresnel += F0;\n"+
	        
	        "float rs = (fresnel * geoAtt * roughness) / (NdotV * NdotL*3.14);\n"+ //componente speculare 
	    	"vec4 finalValue = lightAmbientTotal * NdotL * (k + rs * (1.0 - k));\n"+

	    "gl_FragColor = texture2D(tex_color,f_texcoord)*(diffuseContribute+finalValue);\n"+
	"}";
			
	/*private static String vertexShader="" +
			"uniform mat4 transform;" +
			"uniform mat2 textureTransform;
			"attribute vec3 position;" +
			"attribute vec3 normal;" +
			"attribute vec2 texcoord;"+
			"varying float light;" +
			"varying vec2 ftexcoord;"+
			"void main(){" +
			"	gl_Position=transform*vec4(position,1);" +
			"	vec3 normalVector=(transform*vec4(normal,0)).xyz;" +
			"	normalVector=normalize(normalVector);" +
			"	light=dot(normalVector,vec3(0,0.6,0.0))+0.8;" +
			"	ftexcoord=textureTransform*texcoord;"+
			"}";
		
	
		private static String fragmentShader="" +
				"uniform vec4 color;" +
				"uniform sampler2D uSampler;"+
				"varying float light;" +
				"varying vec2 ftexcoord;"+
				"void main(){" +
				"	gl_FragColor=texture2D(uSampler, vec2(ftexcoord.s, ftexcoord.t))*vec4(vec3(light),1)*color;" +
				"}";
				*/
	
		
		
		private Shader shader;
		private int attribObject;
		private int normalsAttribObject;
		//private int textureAttribObject;
		private int uniformColor;
		private int uniformTransform;
		
		private int textureTransform;
		
		private int lightDir;
		private int lightAmbient;
		private int frontMaterialAmbient;
		private int ambientLight;
		private int diffuseLight;
		
		private int uniformCordTex;
		private int uniformTex;

		private Node nodo;
		private float t=0;
		
		
		/* Drawing method
		 * */
		public void display(GLAutoDrawable arg0) {
			
			//Take the gl elemnt
			GL2ES2 gl=(GL2ES2)arg0.getGL();
			
			gl.getGL2ES2();
			//Clear the image
			gl.glClearColor(1, 1, 1, 1);
			
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
			
			gl.glEnable(GL.GL_DEPTH_TEST);
			gl.glEnable(GL.GL_TEXTURE_2D);
			
			gl.glUseProgram(shader.getShadingProgram());
			
			gl.glUniform4f(uniformColor, 1f, 1f, 1f, 1);
			float scaleFactor=0.03f;
			t+=0.01f;
			float cos=(float)(Math.cos(0.2f+t));
			float sin=(float)(Math.sin(0.2f+t));
			
		/*
			float[] matrix={
					scaleFactor*cos,             0,    -scaleFactor*sin,     0,
					              0,   scaleFactor,                   0,     0,
					scaleFactor*sin,             0,     scaleFactor*cos,     0,
				                  0,             0,                   0,     1,
				};
		*/
		
			float[] matrix={
					scaleFactor*cos, 0, scaleFactor*sin,0,
					scaleFactor*sin,0,-scaleFactor*cos,0,
					0, scaleFactor, 0,0,
					0,0,0,1
			};
			
			float[] position={0.0f,0.0f,-1.0f};
			gl.glUniform3fv(lightDir,1,position,0);
			nodo.setTransformMatrix(matrix);
			nodo.drawNode(gl);
		
		}
		
		public void dispose(GLAutoDrawable arg0) {
			// TODO Auto-generated method stub
			
		}

		/* init method
		 * */
		public void init(GLAutoDrawable arg0) {

			//Take the gl elemnt
			GL2ES2 gl=(GL2ES2)arg0.getGL();
			gl.getGL2ES2();
			shader=new Shader(fragmentShader,vertexShader);
			
			shader.compileShader(gl);
			
			gl.glUseProgram(shader.getShadingProgram());
			
			//------- All this part should be done by the producer ----------- //
			
			VagoneDataReader textureReader=new XMLVagoneDataReader("trainModels/templateVagoneSpace.xml");
			IMultipleSurfaceControlPointsReader vertexReader=new OBJControlPointsMultipleSurfacesReader();
			FixedStepTessellation tess=new FixedStepTessellation(new BorderTextureCoordinateEvaluator());
			tess.setStep_u(0.1f);
			tess.setStep_v(0.1f);
			
			NodoProducer producer=new ClassicCoachProducer(textureReader, vertexReader, tess);
			nodo=producer.getNode(gl);

			//-------- END PRODUCER PART------//
			
			normalsAttribObject=gl.glGetAttribLocation(shader.getShadingProgram(), "normal");
			
			attribObject=gl.glGetAttribLocation(shader.getShadingProgram(), "position");
			
			uniformTransform=gl.glGetUniformLocation(shader.getShadingProgram(), "transform");
			
			//  luci
			lightDir=gl.glGetUniformLocation(shader.getShadingProgram(), "lightDir");	
			lightAmbient=gl.glGetUniformLocation(shader.getShadingProgram(), "ambientLightModel");
			frontMaterialAmbient=gl.glGetUniformLocation(shader.getShadingProgram(), "frontMaterialAmbient");
			ambientLight=gl.glGetUniformLocation(shader.getShadingProgram(), "ambientLightSource");
			diffuseLight=gl.glGetUniformLocation(shader.getShadingProgram(), "diffuseLightSource");
			
			uniformCordTex = gl.glGetAttribLocation(shader.getShadingProgram(), "texcoord");
			//textureAttribObject=gl.glGetAttribLocation(shader.getShadingProgram(), "texcoord");
			uniformTex=gl.glGetUniformLocation(shader.getShadingProgram(), "tex_color");
			//uniformColor=gl.glGetUniformLocation(shader.getShadingProgram(), "color");
			
			textureTransform=gl.glGetUniformLocation(shader.getShadingProgram(), "textureTransform");
			
			//int[] shaderAttribs={attribObject,normalsAttribObject,textureAttribObject,textureTransform, uniformTransform};
			int[] shaderAttribs={attribObject,normalsAttribObject,uniformCordTex,textureTransform,uniformTransform};
			
			
			/*******/
			float ambient[]= {0.9f,0.5f,0.5f,1}; //luce ambientale (GLOBALE)
			//gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT , ambient,0);
			
			gl.glUniform4fv(lightAmbient,1,ambient,0); //il 3fv indica una variabie uniform di tipo vec3

			float materialAmbient[]= {0.5f,0.5f,0.5f,1}; //luce ambientale (GLOBALE) materiale
			//gl.glMaterialfv(GL.GL_FRONT_AND_BACK,GL2.GL_AMBIENT_AND_DIFFUSE,materialAmbient,0 );
			
			gl.glUniform4fv(frontMaterialAmbient,1,materialAmbient,0); //il 3fv indica una variabie uniform di tipo vec3
			
			float ambientLightSource[]={0.5f,0.5f,0.5f,1};
			gl.glUniform4fv(ambientLight,1,ambientLightSource,0); //il 3fv indica una variabie uniform di tipo vec3
			
			float diffuseLightSource[]={0.5f,0.5f,0.5f,1};
			gl.glUniform4fv(diffuseLight,1,diffuseLightSource,0); //il 3fv indica una variabie uniform di tipo vec3

			gl.glUniform1i(uniformTex, 0); //collego la texture --> la prima = 0
			
			nodo.setShaderAttributes(shaderAttribs);
		
		}

		public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) {

		}
	}
	static final long serialVersionUID=100;
}
	
	

